package server.restory;


import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.io.UnsupportedEncodingException;
import java.nio.channels.FileChannel;
import java.security.GeneralSecurityException;
import java.sql.SQLException;
import java.util.logging.Level;
import java.util.logging.Logger;
import capsules.CapsuleTemporarySpecification;
import constans.CapsuleConstans;
import constans.ErrorConstants;
import security.CipherFile;
import security.CipherFileAES;
import security.Signature;
import security.SignatureSHA1;
import database.ConnectionDB;
import database.DatabaseMysql;
import database.DatabaseOperationsCapsules;
import database.DatabaseOperationsCapsulesTemporary;
import java.io.FileReader;
import java.util.Arrays;
import java.util.Properties;
import utils.LogUtils;


public class CapsuleValidator {

    private Logger logger;
	private DatabaseOperationsCapsulesTemporary dbOpCapsulesTemporary;
	private DatabaseOperationsCapsules dbOpCapsules;
	private Signature signature;
	private CipherFile cipher;
	private File folderCapsule, folderCapsuleTemp;
	private long timeSleep;
    private Properties properties;


    public CapsuleValidator(String filename){
        try {
            logger = LogUtils.getLogger("ValidateCapsule", "ValidateCapsule.log");
        } catch (IOException ex) {
            System.exit(1);
        }

         try {
            properties = new Properties();
            properties.load(new FileReader(filename));
        } catch (IOException ex) {
            logger.log(Level.SEVERE, "Error code ("+ErrorConstants.ERROR_CONFIGURATION_FILE+"): "+ex.getMessage());
            System.exit(ErrorConstants.ERROR_CONFIGURATION_FILE);
        }
    }

    
    private void start(){

       dbOpCapsulesTemporary = getDatabaseOperationsCapsulesTemporary();
       dbOpCapsules = getDatabaseOperationsCapsules();
       signature = getSignature();
       cipher = getCipherFilej();
       folderCapsule = getFolderCapsule();
       folderCapsuleTemp = getFolderCapsuleTemporary();
       timeSleep = getTimeSleep();

		restore();
        
	}


	//private 
	private void restore(){

		try {	

			while(true){

				System.out.println("---Start Validation---");

				for(CapsuleTemporarySpecification capsuleTemporary : dbOpCapsulesTemporary.getListCapsuleTemporary()){

					if(!dbOpCapsules.isCapsuleRestore(capsuleTemporary.getName())){

						boolean isRestore = restoreCapsule(capsuleTemporary);

						if(isRestore){
							dbOpCapsules.setCapsuleRestore(capsuleTemporary.getName());							
						    logger.log(Level.INFO,"Valid capsule : "+capsuleTemporary.getName());
						}else{
                              logger.log(Level.INFO,"Invalid capsule : "+capsuleTemporary.getName());
                        }
					}			
			    }
             
               	System.out.println("---Stop Validation---");
                System.out.println();

                //sleep
				Thread.sleep(timeSleep);
            }

		} catch (SQLException ex) {
          logger.log(Level.SEVERE, "Error code ("+ErrorConstants.ERROR_DATABASE+"): "+ex.getMessage());
		  System.exit(ErrorConstants.ERROR_DATABASE);
		} catch (InterruptedException ex) {
		   Logger.getLogger(CapsuleValidator.class.getName()).log(Level.SEVERE, null, ex);
           System.exit(1);//TODO
		} 
	}


	private boolean restoreCapsule(CapsuleTemporarySpecification capsuleTemporary){

		String nameCapsuleTemporary = capsuleTemporary.getName()+capsuleTemporary.getEmail()+ capsuleTemporary.getId()+
		CapsuleConstans.CAPSULE_TEMPORARY_EXTENSION;

		String temporary = capsuleTemporary.getName()+CapsuleConstans.CAPSULE_TEMPORARY_EXTENSION;

		String nameCapsule = capsuleTemporary.getName();

		File capsuleTemp = new File(folderCapsuleTemp, nameCapsuleTemporary);		
		File temp =  new File(folderCapsuleTemp, temporary);		

		boolean isRestore = false;

		try {

			cipher.decrypt(capsuleTemp, temp);

			byte[] signatureArc = getSignature(temp);
			byte[] newSignatureArc = signature.getSignature(temp, 0, temp.length() - SignatureSHA1.keySize);

			if(Arrays.equals(signatureArc, newSignatureArc)){

				File capsule = new File(folderCapsule, nameCapsule);

				copyFile(capsuleTemp, capsule);

				temp.delete();

				isRestore = true;
			}
		}
		catch (IOException e) {
			e.printStackTrace();
		}
		finally{
			temp.delete();
		}

		return isRestore;
	}



	private byte[] getSignature(File file)
            throws FileNotFoundException, IOException{

		RandomAccessFile accessFile = null;		
		try {

			accessFile = new RandomAccessFile(file,"r");
			accessFile.seek(accessFile.length()-SignatureSHA1.keySize);

			byte [] sig= new byte[SignatureSHA1.keySize];

			for(int i = 0 ; i < SignatureSHA1.keySize ; i++)
				sig[i] = (byte) accessFile.readByte();

			accessFile.close();
			accessFile = null;

			return sig;

		} catch (IOException e) {	
			try{
				if(accessFile!=null){
					accessFile.close();
					accessFile = null;
				}
			}catch (IOException e1) {}
			throw new IOException();
		}	
	}



	private void copyFile(File in, File out)
            throws FileNotFoundException,IOException{

		FileChannel srcChannel = null;
		FileChannel dstChannel = null;
		try {

			srcChannel = new FileInputStream(in).getChannel();
			dstChannel = new FileOutputStream(out).getChannel();

			dstChannel.transferFrom(srcChannel, 0, srcChannel.size());

			srcChannel.close();
			srcChannel = null;

			dstChannel.close();
			dstChannel = null;

		} catch (IOException e) {
			try{
				if(srcChannel!=null){
					srcChannel.close();
					srcChannel = null;
				}
			}catch (IOException e1) {}
			try{
				if(dstChannel!=null){
					dstChannel.close();
					dstChannel = null;
				}
			}catch (IOException e1) {}
			throw new IOException();
		}
	}


    private DatabaseOperationsCapsules getDatabaseOperationsCapsules(){

		ConnectionDB connectionDB = null;
        DatabaseOperationsCapsules dbOperationsCapsules = null;
        try {

            connectionDB = new DatabaseMysql(properties.getProperty("DatabaseHost"),
                properties.getProperty("DatabaseUser"), properties.getProperty("DatabasePass"),
                properties.getProperty("DatabaseName")).getConnection();

            dbOperationsCapsules = new DatabaseOperationsCapsules(connectionDB);

        } catch (SQLException ex) {
            if(connectionDB!=null)
                try {
                connectionDB.close();
                } catch (SQLException e) {}

            logger.log(Level.SEVERE, "Error code ("+ErrorConstants.ERROR_DATABASE+"): "+ex.getMessage());
            System.exit(ErrorConstants.ERROR_DATABASE);
        }

        return dbOperationsCapsules;
	}


    private DatabaseOperationsCapsulesTemporary getDatabaseOperationsCapsulesTemporary(){

		ConnectionDB connectionDB = null;
        DatabaseOperationsCapsulesTemporary databaseOperationsCapsulesTemporary = null;
        try {

            connectionDB = new DatabaseMysql(properties.getProperty("DatabaseHost"),
                properties.getProperty("DatabaseUser"), properties.getProperty("DatabasePass"),
                properties.getProperty("DatabaseName")).getConnection();

            databaseOperationsCapsulesTemporary = new DatabaseOperationsCapsulesTemporary(connectionDB);

        } catch (SQLException ex) {
            if(connectionDB!=null)
                try {
                connectionDB.close();
                } catch (SQLException e) {}

            logger.log(Level.SEVERE, "Error code ("+ErrorConstants.ERROR_DATABASE+"): "+ex.getMessage());
            System.exit(ErrorConstants.ERROR_DATABASE);
        }

        return databaseOperationsCapsulesTemporary;
	}


    private Signature getSignature(){

        Signature s =null;
        try {
            s = new SignatureSHA1(properties.getProperty("PassSignature").getBytes("UTF-8"));
        } catch (GeneralSecurityException ex) {
            logger.log(Level.SEVERE, "Error code("+ErrorConstants.ERROR_SIGNATURE_PASSWORD+"): "+ex.getMessage());
            System.exit(ErrorConstants.ERROR_SIGNATURE_PASSWORD);
        } catch (UnsupportedEncodingException ex) {
            //TODO
            System.exit(1);
        }

        return s;
	}

    
    private CipherFile getCipherFilej(){

        CipherFile cf = null;
        try {
                cf = new CipherFileAES(properties.getProperty("PassCipher").getBytes("UTF-8"));
        } catch (GeneralSecurityException ex) {
           logger.log(Level.SEVERE, "Error code("+ErrorConstants.ERROR_CIPHER_PASSWORD+"): "+ex.getMessage());
           System.exit(ErrorConstants.ERROR_CIPHER_PASSWORD);
        } catch (UnsupportedEncodingException ex) {
           //TODO
            System.exit(1);
        }

        return cf;
	}


    private File getFolderCapsule(){
        return new File(properties.getProperty("FolderCapsule"));
    }


    private File getFolderCapsuleTemporary(){
        return new File(properties.getProperty("FolderCapsuleTemporary"));
    }

    
    private long getTimeSleep(){
        Long time = new Long(properties.getProperty("TimeSleep"));

        return time*1000*60;
    }
    
    
    //MAIN
	public static void main(String[] args){
         if (args.length != 1) {
            System.out.println("Sintaxe: java -jar ValidateCapsule.jar <configuration filename>");
        } else {
             new CapsuleValidator(args[0]). start();
        }
		
	}


}
